using System.Collections.Generic;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.nioneo.store
{


	using XAResource = javax.transaction.xa.XAResource;
	using Xid = javax.transaction.xa.Xid;

	using Test = junit.framework.Test;
	using TestSuite = junit.framework.TestSuite;

	using Node = org.neo4j.graphdb.Node;
	using Relationship = org.neo4j.graphdb.Relationship;
	using RelationshipType = org.neo4j.graphdb.RelationshipType;
	using AbstractNeo4jTestCase = org.neo4j.kernel.impl.AbstractNeo4jTestCase;
	using LockReleaser = org.neo4j.kernel.impl.core.LockReleaser;
	using PropertyIndex = org.neo4j.kernel.impl.core.PropertyIndex;
	using NeoStore = org.neo4j.kernel.impl.nioneo.store.NeoStore;
	using PropertyStore = org.neo4j.kernel.impl.nioneo.store.PropertyStore;
	using NeoStoreXaConnection = org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaConnection;
	using NeoStoreXaDataSource = org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource;
	using LockManager = org.neo4j.kernel.impl.transaction.LockManager;
	using XidImpl = org.neo4j.kernel.impl.transaction.XidImpl;

	public class TestXa : AbstractNeo4jTestCase
	{

		public TestXa(string testName) : base(testName)
		{
		}

		static void Main(string[] args)
		{
			junit.textui.TestRunner.run(suite());
		}

		public static Test suite()
		{
			TestSuite suite = new TestSuite(typeof(TestXa));
			return suite;
		}

		private NeoStoreXaDataSource ds;
		private NeoStoreXaConnection xaCon;
		private Logger log;
		private Level level;

		private class MyPropertyIndex : org.neo4j.kernel.impl.core.PropertyIndex
		{
			private static Map<string, PropertyIndex> stringToIndex = new Dictionary<string, PropertyIndex>();
			private static Map<int, PropertyIndex> intToIndex = new Dictionary<int, PropertyIndex>();

			protected internal MyPropertyIndex(string key, int keyId) : base(key, keyId)
			{
			}

			public static Iterable<PropertyIndex> index(string key)
			{
				if (stringToIndex.containsKey(key))
				{
					return Arrays.asList(new PropertyIndex[] { stringToIndex.get(key) });
				}
				return Collections.EMPTY_LIST;
			}

			public static PropertyIndex getIndexFor(int index)
			{
				return intToIndex.get(index);
			}

			public static void add(MyPropertyIndex index)
			{
			// TODO Auto-generated method stub
				stringToIndex.put(index.Key, index);
				intToIndex.put(index.getKeyId(), index);
			}
		}

		private PropertyIndex createDummyIndex(int id, string key)
		{
			MyPropertyIndex index = new MyPropertyIndex(key, id);
			MyPropertyIndex.Add(index);
			return index;
		}

		private LockManager lockManager;
		private LockReleaser lockReleaser;

		public virtual void setUp()
		{
			base.setUp();
			log = Logger.getLogger("org.neo4j.kernel.impl.transaction.xaframework.XaLogicalLog/" + "nioneo_logical.log");
			level = log.getLevel();
			log.setLevel(Level.OFF);
			log = Logger.getLogger("org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource");
			log.setLevel(Level.OFF);
			try
			{
				NeoStore.createStore("neo");
				lockManager = getEmbeddedGraphDb().getConfig().getLockManager();
				lockReleaser = getEmbeddedGraphDb().getConfig().getLockReleaser();
				ds = new NeoStoreXaDataSource("neo", "nioneo_logical.log", lockManager, lockReleaser);
				xaCon = (NeoStoreXaConnection) ds.getXaConnection();
			}
			catch (Exception e)
			{
				fail("" + e);
			}
		}

		public virtual void tearDown()
		{
			base.tearDown();
			ds.Close();
			log.setLevel(level);
			log = Logger.getLogger("org.neo4j.kernel.impl.transaction.xaframework.XaLogicalLog/" + "nioneo_logical.log");
			log.setLevel(level);
			log = Logger.getLogger("org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource");
			log.setLevel(level);
			File file = new File("neo");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.nodestore.db");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.nodestore.db.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.index");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.index.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.index.keys");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.index.keys.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.strings");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.strings.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.arrays");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.propertystore.db.arrays.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.relationshipstore.db");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.relationshipstore.db.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.relationshiptypestore.db");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.relationshiptypestore.db.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.relationshiptypestore.db.names");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("neo.relationshiptypestore.db.names.id");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File(".");
			foreach (File nioFile in file.listFiles())
			{
				if (nioFile.Name.StartsWith("nioneo_logical.log"))
				{
					assertTrue(nioFile.delete());
				}
			}
		}

		private void deleteLogicalLogIfExist()
		{
			File file = new File("nioneo_logical.log.1");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("nioneo_logical.log.2");
			if (file.exists())
			{
				assertTrue(file.delete());
			}
			file = new File("nioneo_logical.log.active");
			assertTrue(file.delete());
		}

		private void renameCopiedLogicalLog()
		{
			File file = new File("nioneo_logical.log.bak.1");
			if (file.exists())
			{
				assertTrue(file.renameTo(new File("nioneo_logical.log.1")));
			}
			else
			{
				file = new File("nioneo_logical.log.bak.2");
				assertTrue(file.renameTo(new File("nioneo_logical.log.2")));
			}
			file = new File("nioneo_logical.log.bak.active");
			assertTrue(file.renameTo(new File("nioneo_logical.log.active")));
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: private void truncateLogicalLog(int size) throws IOException
		private void truncateLogicalLog(int size)
		{
			char active = '1';
			FileChannel af = new RandomAccessFile("nioneo_logical.log.active", "r").getChannel();
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			af.read(buffer);
			af.Close();
			buffer.flip();
			active = buffer.asCharBuffer().get();
			buffer.Clear();
			FileChannel fileChannel = new RandomAccessFile("nioneo_logical.log." + active, "rw").getChannel();
			if (fileChannel.size() > size)
			{
				fileChannel.truncate(size);
			}
			else
			{
				fileChannel.position(size);
				ByteBuffer buf = ByteBuffer.allocate(1);
				buf.put((sbyte) 0).flip();
				fileChannel.write(buf);
			}
			fileChannel.force(false);
			fileChannel.Close();
		}

//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: private void copyLogicalLog() throws IOException
		private void copyLogicalLog()
		{
			char active = '1';
			FileChannel af = new RandomAccessFile("nioneo_logical.log.active", "r").getChannel();
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			af.read(buffer);
			buffer.flip();
			FileChannel activeCopy = new RandomAccessFile("nioneo_logical.log.bak.active", "rw").getChannel();
			activeCopy.write(buffer);
			activeCopy.Close();
			af.Close();
			buffer.flip();
			active = buffer.asCharBuffer().get();
			buffer.Clear();
			FileChannel source = new RandomAccessFile("nioneo_logical.log." + active, "r").getChannel();
			FileChannel dest = new RandomAccessFile("nioneo_logical.log.bak." + active, "rw").getChannel();
			int read = -1;
			do
			{
				read = source.read(buffer);
				buffer.flip();
				dest.write(buffer);
				buffer.Clear();
			}
			while (read == 1024);
			source.Close();
			dest.Close();
		}

		private PropertyIndex index(string key)
		{
			Iterator<PropertyIndex> itr = MyPropertyIndex.index(key).GetEnumerator();
			if (!itr.MoveNext())
			{
				int id = ds.nextId(typeof(PropertyIndex));
				PropertyIndex index = createDummyIndex(id, key);
				xaCon.getPropertyIndexConsumer().createPropertyIndex(id, key);
				return index;
			}
			return itr.Current;
		}

		public virtual void testLogicalLog()
		{
			try
			{
				Xid xid = new XidImpl(new sbyte[1], new sbyte[1]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				int node2 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node2);
				int n1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getNodeConsumer().addProperty(node1, n1prop1, index("prop1"), "string1");
				xaCon.getNodeConsumer().getProperties(node1);
				int relType1 = ds.nextId(typeof(RelationshipType));
				xaCon.getRelationshipTypeConsumer().addRelationshipType(relType1, "relationshiptype1");
				int rel1 = ds.nextId(typeof(Relationship));
				xaCon.getRelationshipConsumer().createRelationship(rel1, node1, node2, relType1);
				int r1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getRelationshipConsumer().addProperty(rel1, r1prop1, index("prop1"), "string1");
				xaCon.getNodeConsumer().changeProperty(node1, n1prop1, "string2");
				xaCon.getRelationshipConsumer().changeProperty(rel1, r1prop1, "string2");
				xaCon.getNodeConsumer().removeProperty(node1, n1prop1);
				xaCon.getRelationshipConsumer().removeProperty(rel1, r1prop1);
				xaCon.getRelationshipConsumer().deleteRelationship(rel1);
				xaCon.getNodeConsumer().deleteNode(node1);
				xaCon.getNodeConsumer().deleteNode(node2);
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaRes.commit(xid, true);
				copyLogicalLog();
				xaCon.clearAllTransactions();
				ds.Close();
				deleteLogicalLogIfExist();
				renameCopiedLogicalLog();
				ds = new NeoStoreXaDataSource("neo", "nioneo_logical.log", lockManager, lockReleaser);
				xaCon = (NeoStoreXaConnection) ds.getXaConnection();
				xaRes = xaCon.getXaResource();
				assertEquals(0, xaRes.recover(XAResource.TMNOFLAGS).length);
				xaCon.clearAllTransactions();
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}

		public virtual void testLogicalLogPrepared()
		{
			try
			{
				Xid xid = new XidImpl(new sbyte[2], new sbyte[2]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				int node2 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node2);
				int n1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getNodeConsumer().addProperty(node1, n1prop1, index("prop1"), "string1");
				int relType1 = ds.nextId(typeof(RelationshipType));
				xaCon.getRelationshipTypeConsumer().addRelationshipType(relType1, "relationshiptype1");
				int rel1 = ds.nextId(typeof(Relationship));
				xaCon.getRelationshipConsumer().createRelationship(rel1, node1, node2, relType1);
				int r1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getRelationshipConsumer().addProperty(rel1, r1prop1, index("prop1"), "string1");
				xaCon.getNodeConsumer().changeProperty(node1, n1prop1, "string2");
				xaCon.getRelationshipConsumer().changeProperty(rel1, r1prop1, "string2");
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaRes.prepare(xid);
				copyLogicalLog();
				xaCon.clearAllTransactions();
				ds.Close();
				deleteLogicalLogIfExist();
				renameCopiedLogicalLog();
				ds = new NeoStoreXaDataSource("neo", "nioneo_logical.log", lockManager, lockReleaser);
				xaCon = (NeoStoreXaConnection) ds.getXaConnection();
				xaRes = xaCon.getXaResource();
				assertEquals(1, xaRes.recover(XAResource.TMNOFLAGS).length);
				xaRes.commit(xid, true);
				xaCon.clearAllTransactions();
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}

		public virtual void testLogicalLogPrePrepared()
		{
			try
			{
				Xid xid = new XidImpl(new sbyte[3], new sbyte[3]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				int node2 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node2);
				int n1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getNodeConsumer().addProperty(node1, n1prop1, index("prop1"), "string1");
				int relType1 = ds.nextId(typeof(RelationshipType));
				xaCon.getRelationshipTypeConsumer().addRelationshipType(relType1, "relationshiptype1");
				int rel1 = ds.nextId(typeof(Relationship));
				xaCon.getRelationshipConsumer().createRelationship(rel1, node1, node2, relType1);
				int r1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getRelationshipConsumer().addProperty(rel1, r1prop1, index("prop1"), "string1");
				xaCon.getNodeConsumer().changeProperty(node1, n1prop1, "string2");
				xaCon.getRelationshipConsumer().changeProperty(rel1, r1prop1, "string2");
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaCon.clearAllTransactions();
				copyLogicalLog();
				ds.Close();
				deleteLogicalLogIfExist();
				renameCopiedLogicalLog();
				ds = new NeoStoreXaDataSource("neo", "nioneo_logical.log", lockManager, lockReleaser);
				xaCon = (NeoStoreXaConnection) ds.getXaConnection();
				xaRes = xaCon.getXaResource();
				assertEquals(0, xaRes.recover(XAResource.TMNOFLAGS).length);
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}

		public virtual void testBrokenNodeCommand()
		{
			try
			{
				Xid xid = new XidImpl(new sbyte[4], new sbyte[4]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaRes.prepare(xid);
				xaCon.clearAllTransactions();
				copyLogicalLog();
				xaCon.clearAllTransactions();
				ds.Close();
				deleteLogicalLogIfExist();
				renameCopiedLogicalLog();
				truncateLogicalLog(39);
				truncateLogicalLog(40);
				ds = new NeoStoreXaDataSource("neo", "nioneo_logical.log", lockManager, lockReleaser);
				xaCon = (NeoStoreXaConnection) ds.getXaConnection();
				xaRes = xaCon.getXaResource();
				assertEquals(0, xaRes.recover(XAResource.TMNOFLAGS).length);
				xaCon.clearAllTransactions();
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}

		public virtual void testBrokenCommand()
		{
			try
			{
				Xid xid = new XidImpl(new sbyte[4], new sbyte[4]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaRes.prepare(xid);
				xaCon.clearAllTransactions();
				copyLogicalLog();
				xaCon.clearAllTransactions();
				ds.Close();
				deleteLogicalLogIfExist();
				renameCopiedLogicalLog();
				truncateLogicalLog(32);
				truncateLogicalLog(40);
				ds = new NeoStoreXaDataSource("neo", "nioneo_logical.log", lockManager, lockReleaser);
				xaCon = (NeoStoreXaConnection) ds.getXaConnection();
				xaRes = xaCon.getXaResource();
				assertEquals(0, xaRes.recover(XAResource.TMNOFLAGS).length);
				xaCon.clearAllTransactions();
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}

		public virtual void testBrokenPrepare()
		{
			try
			{
				Xid xid = new XidImpl(new sbyte[4], new sbyte[4]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				int node2 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node2);
				int n1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getNodeConsumer().addProperty(node1, n1prop1, index("prop1"), "string1");
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaRes.prepare(xid);
				copyLogicalLog();
				xaCon.clearAllTransactions();
				ds.Close();
				deleteLogicalLogIfExist();
				renameCopiedLogicalLog();
				truncateLogicalLog(141);
				ds = new NeoStoreXaDataSource("neo", "nioneo_logical.log", lockManager, lockReleaser);
				xaCon = (NeoStoreXaConnection) ds.getXaConnection();
				xaRes = xaCon.getXaResource();
				assertEquals(0, xaRes.recover(XAResource.TMNOFLAGS).length);
				xaCon.clearAllTransactions();
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}

		public virtual void testBrokenDone()
		{
			try
			{
				Xid xid = new XidImpl(new sbyte[4], new sbyte[4]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				int node2 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node2);
				int n1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getNodeConsumer().addProperty(node1, n1prop1, index("prop1"), "string1");
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaRes.prepare(xid);
				xaRes.commit(xid, false);
				copyLogicalLog();
				ds.Close();
				deleteLogicalLogIfExist();
				renameCopiedLogicalLog();
				truncateLogicalLog(145);
				ds = new NeoStoreXaDataSource("neo", "nioneo_logical.log", lockManager, lockReleaser);
				xaCon = (NeoStoreXaConnection) ds.getXaConnection();
				xaRes = xaCon.getXaResource();
				assertEquals(1, xaRes.recover(XAResource.TMNOFLAGS).length);
				xaCon.clearAllTransactions();
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}

		public virtual void testLogVersion()
		{
			long creationTime = ds.getCreationTime();
			long randomIdentifier = ds.getRandomIdentifier();
			long currentVersion = ds.getCurrentLogVersion();
			assertEquals(currentVersion, ds.incrementAndGetLogVersion());
			assertEquals(currentVersion + 1, ds.incrementAndGetLogVersion());
			assertEquals(creationTime, ds.getCreationTime());
			assertEquals(randomIdentifier, ds.getRandomIdentifier());
		}

		public virtual void testLogicalLogRotation()
		{
			try
			{
				ds.keepLogicalLogs(true);
				Xid xid = new XidImpl(new sbyte[1], new sbyte[1]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				int node2 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node2);
				int n1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getNodeConsumer().addProperty(node1, n1prop1, index("prop1"), "string1");
				xaCon.getNodeConsumer().getProperties(node1);
				int relType1 = ds.nextId(typeof(RelationshipType));
				xaCon.getRelationshipTypeConsumer().addRelationshipType(relType1, "relationshiptype1");
				int rel1 = ds.nextId(typeof(Relationship));
				xaCon.getRelationshipConsumer().createRelationship(rel1, node1, node2, relType1);
				int r1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getRelationshipConsumer().addProperty(rel1, r1prop1, index("prop1"), "string1");
				xaCon.getNodeConsumer().changeProperty(node1, n1prop1, "string2");
				xaCon.getRelationshipConsumer().changeProperty(rel1, r1prop1, "string2");
				xaCon.getNodeConsumer().removeProperty(node1, n1prop1);
				xaCon.getRelationshipConsumer().removeProperty(rel1, r1prop1);
				xaCon.getRelationshipConsumer().deleteRelationship(rel1);
				xaCon.getNodeConsumer().deleteNode(node1);
				xaCon.getNodeConsumer().deleteNode(node2);
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaRes.commit(xid, true);
				long currentVersion = ds.getCurrentLogVersion();
				ds.rotateLogicalLog();
				assertTrue(ds.getLogicalLog(currentVersion) != null);
				ds.rotateLogicalLog();
				assertTrue(ds.getLogicalLog(currentVersion) != null);
				assertTrue(ds.getLogicalLog(currentVersion + 1) != null);
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}


		public virtual void testApplyLogicalLog()
		{
			try
			{
				ds.keepLogicalLogs(true);
				Xid xid = new XidImpl(new sbyte[1], new sbyte[1]);
				XAResource xaRes = xaCon.getXaResource();
				xaRes.Start(xid, XAResource.TMNOFLAGS);
				int node1 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node1);
				int node2 = ds.nextId(typeof(Node));
				xaCon.getNodeConsumer().createNode(node2);
				int n1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getNodeConsumer().addProperty(node1, n1prop1, index("prop1"), "string1");
				xaCon.getNodeConsumer().getProperties(node1);
				int relType1 = ds.nextId(typeof(RelationshipType));
				xaCon.getRelationshipTypeConsumer().addRelationshipType(relType1, "relationshiptype1");
				int rel1 = ds.nextId(typeof(Relationship));
				xaCon.getRelationshipConsumer().createRelationship(rel1, node1, node2, relType1);
				int r1prop1 = ds.nextId(typeof(PropertyStore));
				xaCon.getRelationshipConsumer().addProperty(rel1, r1prop1, index("prop1"), "string1");
				xaCon.getNodeConsumer().changeProperty(node1, n1prop1, "string2");
				xaCon.getRelationshipConsumer().changeProperty(rel1, r1prop1, "string2");
				xaCon.getNodeConsumer().removeProperty(node1, n1prop1);
				xaCon.getRelationshipConsumer().removeProperty(rel1, r1prop1);
				xaCon.getRelationshipConsumer().deleteRelationship(rel1);
				xaCon.getNodeConsumer().deleteNode(node1);
				xaCon.getNodeConsumer().deleteNode(node2);
				xaRes.end(xid, XAResource.TMSUCCESS);
				xaRes.commit(xid, true);
				long currentVersion = ds.getCurrentLogVersion();
				ds.keepLogicalLogs(true);
				ds.rotateLogicalLog();
				ds.rotateLogicalLog();
				ds.rotateLogicalLog();
				ds.setCurrentLogVersion(currentVersion);
				ds.makeBackupSlave();
				ds.applyLog(ds.getLogicalLog(currentVersion));
				ds.applyLog(ds.getLogicalLog(currentVersion + 1));
				ds.applyLog(ds.getLogicalLog(currentVersion + 2));
				ds.keepLogicalLogs(false);
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
		}
	}
}